home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / signals.c < prev    next >
C/C++ Source or Header  |  1996-06-05  |  28KB  |  1,090 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: signals.c,v 4.105 1996/06/05 04:47:09 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. /*======================================================================
  43.      signals.c
  44.      Different signal handlers for different signals
  45.        - Catches all the abort signals, cleans up tty modes and then coredumps
  46.        - Not much to do for SIGHUP
  47.        - Not much to do for SIGTERM
  48.        - turn SIGWINCH into a KEY_RESIZE command
  49.        - No signals for ^Z/suspend, but do it here anyway
  50.        - Also set up the signal handlers, and hold signals for
  51.          critical imap sections of code.
  52.  
  53.  ====*/
  54.  
  55. #include "headers.h"
  56.  
  57. /*
  58.  * call used by TERM and HUP handlers to quickly close streams
  59.  */
  60. void  fast_clean_up PROTO(());
  61. void  suspend_notice PROTO((char *));
  62. void  suspend_warning PROTO(());
  63. void  alarm_signal_reset PROTO(());
  64.  
  65. #if defined(DOS) || defined(OS2)
  66. #define    SIG_PROTO(args) args
  67. #else
  68. #define    SIG_PROTO(args) ()
  69. #endif
  70.  
  71.  
  72.               /* SigType is defined in os.h and is either int or void */
  73. static    SigType    auger_in_signal SIG_PROTO((int));
  74. static    SigType    winch_signal SIG_PROTO((int));
  75. static    SigType    usr2_signal SIG_PROTO((int));
  76. static    SigType    alarm_signal SIG_PROTO((int));
  77. static    SigType    intr_signal SIG_PROTO((int));
  78.  
  79.  
  80. /*----------------------------------------------------------------------
  81.     Install handlers for all the signals we care to catch
  82.   ----------------------------------------------------------------------*/
  83. void
  84. init_signals()
  85. {
  86.     dprint(9, (debugfile, "init_signals()\n"));
  87.     init_sighup();
  88. #ifdef    _WINDOWS
  89.     /* Only one signal works. */
  90.     signal(SIGALRM, (void *)alarm_signal);
  91. #else
  92. #ifdef    OS2
  93.     dont_interrupt();
  94.     signal(SIGALRM, (void *)alarm_signal);
  95. #else
  96. #if    defined(DOS)
  97.     dont_interrupt();
  98. #else
  99. #ifdef DEBUG
  100. #define    CUSHION_SIG    (debug < 7)
  101. #else
  102. #define    CUSHION_SIG    (1)
  103. #endif
  104.  
  105.     if(CUSHION_SIG){
  106.     signal(SIGILL,  auger_in_signal); 
  107.     signal(SIGTRAP, auger_in_signal);
  108.     signal(SIGEMT,  auger_in_signal);
  109.     signal(SIGBUS,  auger_in_signal);
  110.     signal(SIGSEGV, auger_in_signal);
  111.     signal(SIGSYS,  auger_in_signal);
  112.     signal(SIGQUIT, auger_in_signal);
  113.     /* Don't catch SIGFPE cause it's rare and we use it in a hack below*/
  114.     }
  115.     
  116.     init_sigwinch();
  117.  
  118.     /*
  119.      * Set up SIGUSR2 to catch signal from other software using the 
  120.      * c-client to tell us that other access to the folder is being 
  121.      * attempted.  THIS IS A TEST: if it turns out that simply
  122.      * going R/O when another pine is started or the same folder is opened,
  123.      * then we may want to install a smarter handler that uses idle time
  124.      * or even prompts the user to see if it's ok to give up R/O access...
  125.      */
  126.     signal(SIGUSR2, usr2_signal);
  127.     
  128. #ifdef SA_RESTART
  129.     {
  130.     struct sigaction sa;
  131.  
  132.     sa.sa_handler = alarm_signal;
  133.     memset(&sa.sa_mask, 0, sizeof(sa.sa_mask));
  134.     sa.sa_flags = SA_RESTART;
  135.     sigaction(SIGALRM, &sa, NULL);
  136.     }
  137. #else
  138.     signal(SIGALRM, alarm_signal);
  139. #endif
  140.  
  141.     signal(SIGPIPE, SIG_IGN);
  142.     signal(SIGINT, SIG_IGN);
  143.  
  144. #ifdef SIGTSTP
  145.     /* Some unexplained behaviour on Ultrix 4.2 (Hardy) seems to be
  146.        resulting in Pine getting sent a SIGTSTP. Ignore it here.
  147.        probably better to ignore it than let it happen in any case
  148.      */
  149.     signal(SIGTSTP, SIG_IGN); 
  150. #endif /* SIGTSTP */
  151.  
  152. #ifdef    SIGCHLD
  153.     signal(SIGCHLD,  child_signal);
  154. #endif
  155. #endif    /* !DOS */
  156. #endif    /* !OS2 */
  157. #endif    /* !_WINDOWS */
  158. }
  159.  
  160.  
  161.  
  162. /*----------------------------------------------------------------------
  163.     Return all signal handling back to normal
  164.   ----------------------------------------------------------------------*/
  165. void
  166. end_signals(blockem)
  167.     int blockem;
  168. {
  169. #ifdef    _WINDOWS
  170.     signal(SIGALRM, blockem ? SIG_IGN : SIG_DFL);
  171.     signal(SIGHUP,  blockem ? SIG_IGN : SIG_DFL);
  172. #else
  173. #ifdef    OS2
  174.     interrupt_ok();
  175.     signal(SIGALRM, blockem ? SIG_IGN : SIG_DFL);
  176.     signal(SIGHUP,  blockem ? SIG_IGN : SIG_DFL);
  177. #else
  178. #ifdef    DOS
  179.     interrupt_ok();
  180. #else
  181. #ifndef    SIG_ERR
  182. #define    SIG_ERR    (SigType (*)())-1
  183. #endif
  184.  
  185.     dprint(5, (debugfile, "end_signals(%d)\n", blockem));
  186.     if(signal(SIGILL,  blockem ? SIG_IGN : SIG_DFL) == SIG_ERR){
  187.         fprintf(stderr, "Error resetting signals: %s\n",
  188.                 error_description(errno));
  189.         exit(-1);
  190.     }
  191.  
  192.     signal(SIGTRAP, blockem ? SIG_IGN : SIG_DFL);
  193.     signal(SIGEMT,  blockem ? SIG_IGN : SIG_DFL);
  194.     signal(SIGBUS,  blockem ? SIG_IGN : SIG_DFL);
  195.     signal(SIGSEGV, blockem ? SIG_IGN : SIG_DFL);
  196.     signal(SIGSYS,  blockem ? SIG_IGN : SIG_DFL);
  197. #ifdef RESIZING
  198.     signal(SIGWINCH, blockem ? SIG_IGN : SIG_DFL);
  199. #endif
  200.     signal(SIGQUIT, blockem ? SIG_IGN : SIG_DFL);
  201. #ifdef SIGTSTP
  202.     signal(SIGTSTP, blockem ? SIG_IGN : SIG_DFL);
  203. #endif /* SIGTSTP */
  204.     signal(SIGHUP,  blockem ? SIG_IGN : SIG_DFL);
  205.     signal(SIGALRM, blockem ? SIG_IGN : SIG_DFL);
  206.     signal(SIGTERM, blockem ? SIG_IGN : SIG_DFL);
  207.     signal(SIGINT,  blockem ? SIG_IGN : SIG_DFL);
  208. #endif    /* !DOS */
  209. #endif    /* !OS2 */
  210. #endif    /* !_WINDOWS */
  211. }
  212.  
  213.  
  214. /*----------------------------------------------------------------------
  215.      Handle signals caused by aborts -- SIGSEGV, SIGILL, etc
  216.  
  217. Call panic which cleans up tty modes and then core dumps
  218.   ----------------------------------------------------------------------*/
  219. static SigType
  220. auger_in_signal SIG_PROTO ((int sig))
  221. {
  222.     end_signals(1);            /* don't catch any more signals */
  223.     dprint(5, (debugfile, "auger_in_signal()\n"));
  224.     panic("Received abort signal");    /* clean up and get out */
  225.     exit(-1);                /* in case panic doesn't kill us */
  226. }
  227.  
  228.  
  229. /*----------------------------------------------------------------------
  230.    Install signal handler to deal with hang up signals -- SIGHUP, SIGTERM
  231.   
  232.   ----------------------------------------------------------------------*/
  233. void
  234. init_sighup()
  235. {
  236. #if    !(defined(DOS) && !defined(_WINDOWS))
  237. #if    defined(_WINDOWS) || defined(OS2)
  238.     signal(SIGHUP, (void *) hup_signal);
  239. #else
  240.     signal(SIGHUP, hup_signal);
  241. #endif
  242. #endif
  243. #if    !(defined(DOS) || defined(OS2))
  244.     signal(SIGTERM, term_signal);
  245. #endif
  246. }
  247.  
  248.  
  249. /*----------------------------------------------------------------------
  250.    De-Install signal handler to deal with hang up signals -- SIGHUP, SIGTERM
  251.   
  252.   ----------------------------------------------------------------------*/
  253. void
  254. end_sighup()
  255. {
  256. #if    !(defined(DOS) && !defined(_WINDOWS))
  257.     signal(SIGHUP, SIG_IGN);
  258. #endif
  259. #if    !(defined(DOS) || defined(OS2))
  260.     signal(SIGTERM, SIG_IGN);
  261. #endif
  262. }
  263.  
  264.  
  265. /*----------------------------------------------------------------------
  266.       handle hang up signal -- SIGHUP
  267.  
  268. Not much to do. Rely on periodic mail file check pointing.
  269.   ----------------------------------------------------------------------*/
  270. SigType
  271. hup_signal()
  272. {
  273. #if    !defined(DOS) || defined(_WINDOWS)
  274.     end_signals(1);            /* don't catch any more signals */
  275.     dprint(1, (debugfile, "\n\n** Received SIGHUP **\n\n\n\n"));
  276.     fast_clean_up();
  277.     printf("\n\nPine finished. Received hang up signal\n\n");
  278. #endif    /* !DOS */
  279.     exit(0);
  280. }
  281.  
  282.  
  283. /*----------------------------------------------------------------------
  284.       handle terminate signal -- SIGTERM
  285.  
  286. Not much to do. Rely on periodic mail file check pointing.
  287.   ----------------------------------------------------------------------*/
  288. SigType
  289. term_signal()
  290. {
  291. #if !defined(DOS) && !defined(OS2)
  292.     end_signals(1);            /* don't catch any more signals */
  293.     dprint(1, (debugfile, "\n\n** Received SIGTERM **\n\n\n\n"));
  294.     fast_clean_up();
  295.     printf("\n\nPine finished. Received terminate signal\n\n");
  296. #endif    /* !DOS */
  297.     exit(0);
  298. }
  299.  
  300.  
  301. /*----------------------------------------------------------------------
  302.      Handle cleaning up mail streams and tty modes...
  303. Not much to do. Rely on periodic mail file check pointing.  Don't try
  304. cleaning up screen or flushing output since stdout is likely already
  305. gone.  To be safe, though, we'll at least restore the original tty mode.
  306.   ----------------------------------------------------------------------*/
  307. void
  308. fast_clean_up()
  309. {
  310.     if(ps_global->expunge_in_progress){
  311.     Raw(0);
  312.     return;
  313.     }
  314.  
  315. #if !defined(DOS) && !defined(OS2)
  316.     if(ps_global->inbox_stream != NULL && !ps_global->inbox_stream->lock){
  317.         if(ps_global->inbox_stream == ps_global->mail_stream)
  318.           ps_global->mail_stream = NULL; 
  319.         mail_close(ps_global->inbox_stream);
  320.     }
  321.  
  322.     if(ps_global->mail_stream != NULL &&
  323.        ps_global->mail_stream != ps_global->inbox_stream &&
  324.        !ps_global->mail_stream->lock)
  325.       mail_close(ps_global->mail_stream);
  326.  
  327.     Raw(0);
  328.  
  329. #endif    /* !DOS */
  330. #if    defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
  331.     if(debugfile)
  332.       fclose(debugfile);
  333. #endif 
  334. }
  335.  
  336.  
  337. #if !defined(DOS) && !defined(OS2)
  338. /*----------------------------------------------------------------------
  339.       handle hang up signal -- SIGUSR2
  340.  
  341. Not much to do. Rely on periodic mail file check pointing.
  342.   ----------------------------------------------------------------------*/
  343. static SigType
  344. usr2_signal SIG_PROTO((int sig))
  345. {
  346.     char c;
  347.     dprint(1, (debugfile, "\n\n** Received SIGUSR2 **\n\n\n\n"));
  348.  
  349.     if(ps_global->inbox_stream
  350.        && !ps_global->inbox_stream->lock
  351.        && !ps_global->inbox_stream->rdonly
  352.        && (c = *ps_global->inbox_stream->mailbox) != '{' && c != '*'){
  353.     mail_check(ps_global->inbox_stream);    /* write latest state   */
  354.     ps_global->inbox_stream->rdonly = 1;    /* and become read-only */
  355.     mail_ping(ps_global->inbox_stream);
  356.     q_status_message(SM_ASYNC, 3, 7,
  357.            "Another Pine is accessing Inbox.  Session now Read-Only.");
  358.     dprint(1, (debugfile, "** INBOX went read-only **\n\n"));
  359.     }
  360.  
  361.     if(ps_global->mail_stream
  362.        && !ps_global->mail_stream->lock
  363.        && !ps_global->mail_stream->rdonly
  364.        && (c = *ps_global->mail_stream->mailbox) != '{' && c != '*'){
  365.     mail_check(ps_global->mail_stream);    /* write latest state   */
  366.     ps_global->mail_stream->rdonly = 1;    /* and become read-only */
  367.     mail_ping(ps_global->mail_stream);
  368.     q_status_message(SM_ASYNC, 3, 7,
  369.           "Another Pine is accessing folder.  Session now Read-Only.");
  370.     dprint(1, (debugfile, "** secondary folder went read-only **\n\n"));
  371.     }
  372. }
  373. #endif
  374.  
  375.  
  376.  
  377. /*----------------------------------------------------------------------
  378.    Install signal handler to deal with window resize signal -- SIGWINCH
  379.   
  380.   ----------------------------------------------------------------------*/
  381. void
  382. init_sigwinch ()
  383. {
  384. #ifdef RESIZING
  385.     signal(SIGWINCH, winch_signal);
  386. #endif
  387. }
  388.  
  389.  
  390. #ifdef RESIZING
  391. /*----------------------------------------------------------------------
  392.    Handle window resize signal -- SIGWINCH
  393.   
  394.    The planned strategy is just force a redraw command. This is similar
  395.   to new mail handling which forces a noop command. The signals are
  396.   help until pine reads input. Then a KEY_RESIZE is forced into the command
  397.   stream .
  398.    Note that ready_for_winch is only non-zero inside the read_char function,
  399.   so the longjmp only ever happens there, and it is really just a jump
  400.   from lower down in the function up to the top of that function.  Its
  401.   purpose is to return a KEY_RESIZE from read_char when interrupted
  402.   out of the select lower down in read_char.
  403.   ----------------------------------------------------------------------*/
  404. extern jmp_buf  winch_state;
  405. extern int      ready_for_winch, winch_occured;
  406.  
  407. SigType
  408. static winch_signal SIG_PROTO((int sig))
  409. {
  410.     dprint(9,(debugfile, "SIGWINCH ready_for_winch: %d winch_occured:%d\n",
  411.                ready_for_winch, winch_occured));
  412.     clear_cursor_pos();
  413.     init_sigwinch();
  414.     if(ready_for_winch)
  415.       longjmp(winch_state, 1);
  416.     else
  417.       winch_occured = 1;
  418. }
  419. #endif
  420.  
  421.  
  422. #ifdef    SIGCHLD
  423. /*----------------------------------------------------------------------
  424.    Handle child status change -- SIGCHLD
  425.   
  426.    The strategy here is to install the handler when we spawn a child, and
  427.    to let the system tell us when the child's state has changed.  In the
  428.    mean time, we can do whatever.  Typically, "whatever" is spinning in a
  429.    loop alternating between sleep and new_mail calls intended to keep the
  430.    IMAP stream alive.
  431.  
  432.   ----------------------------------------------------------------------*/
  433. extern short    child_signalled, child_jump;
  434. extern jmp_buf  child_state;
  435.  
  436. SigType
  437. child_signal()
  438. {
  439.     dprint(9,(debugfile, "SIGCHLD raised\n"));
  440.  
  441. #ifdef    BACKGROUND_POST
  442.     if(post_reap())
  443.       return;
  444. #endif
  445.  
  446.     child_signalled = 1;
  447.     if(child_jump)
  448.       longjmp(child_state, 1);
  449. }
  450. #endif
  451.  
  452.  
  453. #define MAX_BM          80  /* max length of busy message */
  454. static unsigned       alarm_increment;
  455. static int            dotcount;
  456. static char           busy_message[MAX_BM + 1];
  457. static int            busy_alarm_outstanding;
  458. static int            busy_len;
  459. static int            final_message;
  460. static int            callcount;
  461. static percent_done_t percent_done_ptr;
  462. static char *display_chars[] = {
  463.     "<\\> ",
  464.     "<|> ",
  465.     "</> ",
  466.     "<-> ",
  467.     "    ",
  468.     "<-> "
  469. };
  470. #define DISPLAY_CHARS_ROWS 6
  471. #define DISPLAY_CHARS_COLS 4
  472.  
  473. /*
  474.  * Turn on a busy alarm.
  475.  *
  476.  *    seconds -- alarm fires every seconds seconds
  477.  *        msg -- the busy message to print in status line
  478.  *    pc_func -- if non-null, call this function to get the percent done,
  479.  *           (an integer between 0 and 100).  If null, append dots.
  480.  *   init_msg -- if non-zero, force out an immediate status message instead
  481.  *                 of waiting for first alarm (except see comments in code)
  482.  *
  483.  *   Returns:  0 If busy alarm was already set up before we got here
  484.  *             1 If busy alarm was not already set up.
  485.  */
  486. int
  487. busy_alarm(seconds, msg, pc_func, init_msg)
  488.     unsigned       seconds;
  489.     char          *msg;
  490.     percent_done_t pc_func;
  491.     int            init_msg;
  492. {
  493.     int retval = 1;
  494.  
  495.     dprint(9,(debugfile, "busy_alarm(%d, %s, %p, %d)\n",
  496.     seconds, msg ? msg : "Busy", pc_func, init_msg));
  497.  
  498.     /*
  499.      * If we're already busy'ing, and we don't have something special,
  500.      * just leave it alone.
  501.      */
  502.     if(busy_alarm_outstanding){
  503.     retval = 0;
  504.     if(!msg && !pc_func)
  505.       return(retval);
  506.     }
  507.  
  508.     if(seconds){
  509.     busy_alarm_outstanding = 1;
  510.     alarm_increment = seconds;
  511.     dotcount = 0;
  512.     percent_done_ptr = pc_func;
  513.     callcount = 0;
  514.  
  515.     if(msg){
  516.         strncpy(busy_message, msg, MAX_BM);
  517.         final_message = 1;
  518.     }
  519.     else{
  520.         strcpy(busy_message, "Busy");
  521.         final_message = 0;
  522.     }
  523.  
  524.     busy_message[MAX_BM] = '\0';
  525.     busy_len = strlen(busy_message);
  526.  
  527.     if(init_msg){
  528.         char progress[MAX_SCREEN_COLS+1];
  529.         int space_left, slots_used;
  530.  
  531.         final_message = 1;
  532.         space_left = (ps_global->ttyo ? ps_global->ttyo->screen_cols
  533.                       : 80) -
  534.                       busy_len - 2;  /* 2 is for [] */
  535.         slots_used = max(0, min(space_left-3, 10));
  536.  
  537.         if(percent_done_ptr && slots_used >= 4){
  538.         sprintf(progress, "%s |%*s|", busy_message, slots_used, "");
  539.         q_status_message(SM_ORDER, 0, 1, progress);
  540.         }
  541.         else{
  542.         dotcount++;
  543.         sprintf(progress, "%s%*s", busy_message,
  544.             DISPLAY_CHARS_COLS + 1, "");
  545.         q_status_message(SM_ORDER, 0, 1, progress);
  546.         }
  547.  
  548.         /*
  549.          * We use display_message so that the initial message will
  550.          * be forced out only if there is not a previous message
  551.          * currently being displayed that hasn't been displayed for
  552.          * its min display time yet.  In that case, we don't want
  553.          * to force out the initial message.
  554.          */
  555.         display_message('x');
  556.     }
  557.     
  558. #ifdef _WINDOWS
  559.     mswin_setcursor (MSWIN_CURSOR_BUSY);
  560. #endif
  561.     fflush(stdout);
  562.     }
  563.  
  564.     /* set alarm */
  565.     if(F_OFF(F_DISABLE_ALARM, ps_global))
  566.       (void)alarm(seconds);
  567.  
  568.     return(retval);
  569. }
  570.  
  571.  
  572. /*
  573.  * If final_message was set when busy_alarm was called:
  574.  *   and message_pri = -1 -- no final message queued
  575.  *                 else final message queued with min equal to message_pri
  576.  */
  577. void
  578. cancel_busy_alarm(message_pri)
  579.     int message_pri;
  580. {
  581.     dprint(9,(debugfile, "cancel_busy_alarm(%d)\n", message_pri));
  582.  
  583.     (void)alarm(0);
  584.  
  585.     if(busy_alarm_outstanding){
  586.     int space_left, slots_used;
  587.  
  588.     busy_alarm_outstanding = 0;
  589.  
  590.     if(final_message && message_pri >= 0){
  591.         char progress[MAX_SCREEN_COLS+1];
  592.  
  593.         space_left = (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) -
  594.         busy_len - 2;  /* 2 is for [] */
  595.         slots_used = max(0, min(space_left-3, 10));
  596.  
  597.         if(percent_done_ptr && slots_used >= 4){
  598.         int left, right;
  599.  
  600.         right = (slots_used - 4)/2;
  601.         left  = slots_used - 4 - right;
  602.         sprintf(progress, "%s |%*s100%%%*s|",
  603.             busy_message, left, "", right, "");
  604.         q_status_message(SM_ORDER,
  605.             message_pri>=2 ? max(message_pri,3) : 0,
  606.             message_pri+2, progress);
  607.         }
  608.         else{
  609.         sprintf(progress, "%s%*sDONE", busy_message,
  610.             DISPLAY_CHARS_COLS - 4 + 1, "");
  611.         q_status_message(SM_ORDER,
  612.             message_pri>=2 ? max(message_pri,3) : 0,
  613.             message_pri+2, progress);
  614.         }
  615.     }
  616.     else
  617.       mark_status_dirty();
  618.     }
  619. }
  620.  
  621.  
  622. /*
  623.  * suspend_busy_alarm - continue previously installed busy_alarm.
  624.  */
  625. void
  626. suspend_busy_alarm()
  627. {
  628.     dprint(9,(debugfile, "suspend_busy_alarm\n"));
  629.  
  630.     if(busy_alarm_outstanding)
  631.       alarm(0);
  632. }
  633.  
  634.  
  635. /*
  636.  * resume_busy_alarm - continue previously installed busy_alarm.
  637.  */
  638. void
  639. resume_busy_alarm()
  640. {
  641.     dprint(9,(debugfile, "resume_busy_alarm\n"));
  642.  
  643.     if(busy_alarm_outstanding && F_OFF(F_DISABLE_ALARM, ps_global))
  644.       (void)alarm(alarm_increment);
  645. }
  646.  
  647.  
  648. static SigType
  649. alarm_signal SIG_PROTO((int sig))
  650. {
  651.     int space_left, slots_used;
  652.     char dbuf[MAX_SCREEN_COLS+1];
  653.  
  654.     dprint(9,(debugfile, "alarm_signal()\n"));
  655.  
  656.     /*
  657.      * In case something was queue'd before this alarm's busy_handler
  658.      * was installed.  If so, just act like nothing happened...
  659.      */
  660.     if(status_message_remaining()){
  661.     alarm_signal_reset();
  662.     return;
  663.     }
  664.  
  665.     space_left = (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) -
  666.     busy_len - 2;  /* 2 is for [] */
  667.     slots_used = max(0, min(space_left-3, 10));
  668.  
  669.     if(percent_done_ptr && slots_used >= 4){
  670.     int completed, pd;
  671.     char *s;
  672.  
  673.     pd = (*percent_done_ptr)();
  674.     pd = min(max(0, pd), 100);
  675.  
  676.     completed = (pd * slots_used) / 100;
  677.     sprintf(dbuf, "%s |%s%s%*s|", busy_message,
  678.         completed > 1 ? repeat_char(completed-1, pd==100 ? ' ' : '-') : "",
  679.         (completed > 0 && pd != 100) ? ">" : "",
  680.         slots_used - completed, "");
  681.  
  682.     if(slots_used == 10){
  683.         s = dbuf + strlen(dbuf) - 8;
  684.         if(pd < 10){
  685.         s++; s++;
  686.         *s++ = '0' + pd;
  687.         }
  688.         else if(pd < 100){
  689.         s++;
  690.         *s++ = '0' + pd / 10;
  691.         *s++ = '0' + pd % 10;
  692.         }
  693.         else{
  694.         *s++ = '1';
  695.         *s++ = '0';
  696.         *s++ = '0';
  697.         }
  698.  
  699.         *s   = '%';
  700.     }
  701.     }
  702.     else{
  703.     char b[DISPLAY_CHARS_COLS + 2];
  704.     int md = DISPLAY_CHARS_ROWS - 2;
  705.     int ind;
  706.  
  707.     ind = (dotcount == 0) ? md :
  708.            (dotcount == 1) ? md + 1 : ((dotcount-2) % md);
  709.  
  710.     if(space_left >= DISPLAY_CHARS_COLS + 1){
  711.         b[0] = SPACE;
  712.         strcpy(b+1, display_chars[ind]);
  713.     }
  714.     else if(space_left >= 2){
  715.         b[0] = '.';
  716.         b[1] = '.';
  717.         b[2] = '.';
  718.         b[space_left] = '\0';
  719.     }
  720.     else
  721.       b[0] = '\0';
  722.  
  723.     sprintf(dbuf, "%s%s", busy_message, b);
  724.     }
  725.  
  726.     status_message_write(dbuf, 1);
  727.     dotcount++;
  728.     fflush(stdout);
  729.  
  730.     alarm_signal_reset();
  731. }
  732.  
  733.  
  734. /*
  735.  * alarm_signal_reset - prepare for another alarm
  736.  */
  737. void
  738. alarm_signal_reset()
  739. {
  740.     if(F_ON(F_DISABLE_ALARM, ps_global))
  741.       return;
  742.  
  743. #if    !defined(DOS) || defined(_WINDOWS)
  744.     signal(SIGALRM, alarm_signal);
  745. #endif
  746.     (void)alarm(alarm_increment);
  747. }
  748.  
  749.  
  750. #ifdef    DOS
  751. /*
  752.  * For systems that don't have an alarm() call.
  753.  */
  754. void
  755. fake_alarm_blip()
  756. {
  757.     static time_t   last_alarm = -1;
  758.     time_t        now;
  759.  
  760.     if(busy_alarm_outstanding &&
  761.        ++callcount % 5 == 0 &&
  762.        (long)((now=time((time_t *)0)) - last_alarm) >= (long)alarm_increment){
  763.     last_alarm = now;
  764.     /* manually execute code that an alarm would cause to execute */
  765.     alarm_signal(1);
  766.     }
  767. }
  768. #endif
  769.  
  770.  
  771. /*
  772.  * Command interrupt support.
  773.  */
  774.  
  775. static SigType
  776. intr_signal SIG_PROTO((int sig))
  777. {
  778.     ps_global->intr_pending = 1;
  779. }
  780.  
  781.  
  782. void
  783. intr_allow()
  784. {
  785.     if(signal(SIGINT, intr_signal) == intr_signal)
  786.       return;                /* already installed */
  787.  
  788.     intr_proc(1);            /* turn on interrupt char */
  789. }
  790.  
  791.  
  792. void
  793. intr_disallow()
  794. {
  795.     if(signal(SIGINT, SIG_IGN) == SIG_IGN)    /* already off! */
  796.       return;
  797.  
  798.     pine_sigunblock(SIGINT);        /* unblock signal after longjmp */
  799.     ps_global->intr_pending = 0;
  800.     intr_proc(0);            /* turn off interrupt char */
  801. }
  802.  
  803.  
  804. void
  805. intr_handling_on()
  806. {
  807.     if(signal(SIGINT, intr_signal) == intr_signal)
  808.       return;                /* already installed */
  809.  
  810.     intr_proc(1);
  811.     if(ps_global && ps_global->ttyo)
  812.       draw_cancel_keymenu();
  813. }
  814.  
  815.  
  816. void
  817. intr_handling_off()
  818. {
  819.     if(signal(SIGINT, SIG_IGN) == SIG_IGN)    /* already off! */
  820.       return;
  821.  
  822.     ps_global->intr_pending = 0;
  823.     intr_proc(0);
  824.     if(ps_global && ps_global->ttyo)
  825.       blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
  826.  
  827.     ps_global->mangled_footer = 1;
  828. }
  829.  
  830.  
  831. /*----------------------------------------------------------------------
  832.      Suspend Pine. Reset tty and suspend. Suspend is finished when this returns
  833.  
  834.    Args:  The pine structure
  835.  
  836.  Result:  Execution suspended for a while. Screen will need redrawing 
  837.           after this is done.
  838.  
  839.  Instead of the usual handling of ^Z by catching a signal, we actually read
  840. the ^Z and then clean up the tty driver, then kill ourself to stop, and 
  841. pick up where we left off when execution resumes.
  842.   ----------------------------------------------------------------------*/
  843. int
  844. do_suspend(pine) 
  845.     struct pine *pine;
  846. {
  847.     time_t now;
  848.     int   result, isremote, retval;
  849.     int   orig_cols, orig_rows;
  850. #ifdef    DOS
  851.     static char *shell = NULL;
  852. #define    STD_SHELL    "COMMAND.COM"
  853. #else
  854. #ifdef    OS2
  855.     static char *shell = NULL;
  856. #define    STD_SHELL    "CMD.EXE"
  857. #else
  858.     char *shell;
  859. #endif
  860. #endif
  861.  
  862.     if(!have_job_control()){
  863.     bogus_command(ctrl('Z'), F_ON(F_USE_FK, pine) ? "F1" : "?");
  864.     return(NO_OP_COMMAND);
  865.     }
  866.  
  867.     if(F_OFF(F_CAN_SUSPEND, pine)){
  868.     q_status_message(SM_ORDER | SM_DING, 3, 3,
  869.              "Pine suspension not enabled - see help text");
  870.     return(NO_OP_COMMAND);
  871.     }
  872.  
  873. #ifdef    _WINDOWS
  874.     /* suspend what? */
  875. #else
  876.  
  877.     isremote = (ps_global->mail_stream && ps_global->mail_stream->mailbox
  878.         && (*ps_global->mail_stream->mailbox == '{'
  879.             || (*ps_global->mail_stream->mailbox == '*'
  880.             && *(ps_global->mail_stream->mailbox + 1) == '{')));
  881.  
  882.     now = time((time_t *)0);
  883.     dprint(1, (debugfile, "\n\n --- %s - SUSPEND ----  %s",
  884.            isremote ? "REMOTE" : "LOCAL", ctime(&now)));
  885.     EndInverse();
  886.     end_keyboard(F_ON(F_USE_FK,pine));
  887.     end_tty_driver(pine);
  888.     end_screen(NULL);
  889. #if defined(DOS) || defined(OS2)
  890. #ifdef OS2
  891.     interrupt_ok();
  892. #endif
  893.     suspend_notice("exit");
  894.     if (!shell){
  895.     char *p;
  896.  
  897.     if (!((shell = getenv("SHELL")) || (shell = getenv("COMSPEC"))))
  898.       shell = STD_SHELL;
  899.  
  900.     shell = cpystr(shell);            /* copy in free storage */
  901.     for(p = shell; p = strchr(p, '/'); p++)
  902.       *p = '\\';
  903.     }
  904.  
  905.     result = system(shell);
  906. #else
  907.     if(F_ON(F_SUSPEND_SPAWNS, ps_global)){
  908.     PIPE_S *syspipe;
  909.  
  910.     if(syspipe = open_system_pipe(NULL, NULL, NULL, PIPE_USER|PIPE_RESET)){
  911.         suspend_notice("exit");
  912. #ifndef    SIGCHLD
  913.         if(isremote)
  914.           suspend_warning();
  915. #endif
  916.         (void) close_system_pipe(&syspipe);
  917.     }
  918.     }
  919.     else{
  920.     suspend_notice("fg");
  921.  
  922.     if(isremote)
  923.       suspend_warning();
  924.  
  925.     stop_process();
  926.     }
  927. #endif    /* DOS */
  928.  
  929.     now = time((time_t *)0);
  930.     dprint(1, (debugfile, "\n\n ---- RETURN FROM SUSPEND ----  %s",
  931.                ctime(&now)));
  932. #ifdef OS2
  933.     enter_text_mode(NULL);
  934. #endif
  935.     init_screen();
  936.     init_tty_driver(pine);
  937.     init_keyboard(F_ON(F_USE_FK,pine));
  938.  
  939. #ifdef OS2
  940.     dont_interrupt();
  941. #endif
  942.     orig_cols = pine->ttyo->screen_cols;
  943.     orig_rows = pine->ttyo->screen_rows;
  944.     fix_windsize(pine);
  945. #if defined(DOS) || defined(OS2)
  946.     if(orig_cols != pine->ttyo->screen_cols ||
  947.        orig_rows != pine->ttyo->screen_rows)
  948.     retval = KEY_RESIZE;
  949.     else
  950. #endif
  951.     retval = ctrl('L');;
  952.  
  953. #if    defined(DOS) || defined(OS2)
  954.     if(result == -1)
  955.       q_status_message1(SM_ORDER | SM_DING, 3, 4,
  956.             "Error loading \"%s\"", shell);
  957. #endif
  958.  
  959.     if(isremote && (char *)mail_ping(ps_global->mail_stream) == NULL)
  960.       q_status_message(SM_ORDER | SM_DING, 4, 9,
  961.                "Suspended for too long, IMAP connection broken");
  962.  
  963.     return(retval);
  964. #endif    /* !_WINDOWS */
  965. }
  966.  
  967.  
  968.  
  969. /*----------------------------------------------------------------------
  970.  ----*/
  971. void
  972. suspend_notice(s)
  973.     char *s;
  974. {
  975.     printf("\nPine suspended. Give the \"%s\" command to come back.\n", s);
  976.     fflush(stdout);
  977. }
  978.  
  979.  
  980.  
  981. /*----------------------------------------------------------------------
  982.  ----*/
  983. void
  984. suspend_warning()
  985. {
  986.     puts("Warning: Your IMAP connection will be closed if Pine");
  987.     puts("is suspended for more than 30 minutes\n");
  988.     fflush(stdout);
  989. }
  990.  
  991.  
  992.  
  993. /*----------------------------------------------------------------------
  994.  ----*/
  995. void
  996. fix_windsize(pine)
  997.     struct pine *pine;
  998. {
  999.     mark_keymenu_dirty();
  1000.     mark_status_dirty();
  1001.     mark_titlebar_dirty();
  1002.     clear_cursor_pos();
  1003.     get_windsize(pine->ttyo);
  1004. }
  1005.  
  1006.  
  1007. #if defined(DOS) || defined(OS2)
  1008. SigType (*hold_int)(int), (*hold_term)(int), (*hold_quit)(int);
  1009. #else
  1010. SigType (*hold_hup)(), (*hold_int)(), (*hold_term)(), (*hold_usr2)(),
  1011.     (*hold_quit)();
  1012. #endif
  1013.  
  1014. /*----------------------------------------------------------------------
  1015.      Ignore signals when imap is running through critical code
  1016.  
  1017.  Args: stream -- The stream on which critical operation is proceeding
  1018.  ----*/
  1019.  
  1020. void 
  1021. mm_critical(stream)
  1022.      MAILSTREAM *stream;
  1023. {
  1024.     stream = stream; /* For compiler complaints that this isn't used */
  1025. #if !defined(DOS) && !defined(OS2)
  1026.     hold_hup  = signal(SIGHUP, SIG_IGN);
  1027.     hold_usr2 = signal(SIGUSR2, SIG_IGN);
  1028. #endif
  1029.     hold_int  = signal(SIGINT, SIG_IGN);
  1030.     hold_term = signal(SIGTERM, SIG_IGN);
  1031.     dprint(9, (debugfile, "Done with IMAP critical on %s\n",
  1032.               stream ? stream->mailbox : "<no folder>" ));
  1033. }
  1034.  
  1035.  
  1036.  
  1037. /*----------------------------------------------------------------------
  1038.    Reset signals after critical imap code
  1039.  ----*/
  1040. void
  1041. mm_nocritical(stream)
  1042.      MAILSTREAM *stream;
  1043.     stream = stream; /* For compiler complaints that this isn't used */
  1044.  
  1045. #if !defined(DOS) && !defined(OS2)
  1046.     (void)signal(SIGHUP, hold_hup);
  1047.     (void)signal(SIGUSR2, hold_usr2);
  1048. #endif
  1049.     (void)signal(SIGINT, hold_int);
  1050.     (void)signal(SIGTERM, hold_term);
  1051.     dprint(9, (debugfile, "Done with IMAP critical on %s\n",
  1052.               stream ? stream->mailbox : "<no folder>" ));
  1053. }
  1054.  
  1055.  
  1056. #ifdef    POSIX_SIGNALS
  1057. /*----------------------------------------------------------------------
  1058.    Reset signals after critical imap code
  1059.  ----*/
  1060. SigType
  1061. (*posix_signal(sig_num, action))()
  1062.     int        sig_num;
  1063.     SigType (*action)();
  1064. {
  1065.     struct sigaction new_action, old_action;
  1066.  
  1067.     sigemptyset (&new_action.sa_mask);
  1068.     new_action.sa_handler = action;
  1069. #ifdef    SA_RESTART
  1070.     new_action.sa_flags = SA_RESTART;
  1071. #else
  1072.     new_action.sa_flags = 0;
  1073. #endif
  1074.     sigaction(sig_num, &new_action, &old_action);
  1075.     return(old_action.sa_handler);
  1076. }
  1077.  
  1078. int
  1079. posix_sigunblock(mask)
  1080.     int mask;
  1081. {
  1082.     sigset_t sig_mask;
  1083.  
  1084.     sigemptyset(&sig_mask);
  1085.     sigaddset(&sig_mask, mask);
  1086.     return(sigprocmask(SIG_UNBLOCK, &sig_mask, NULL));
  1087. }
  1088. #endif /* POSIX_SIGNALS */
  1089.